package com.gzsxy.esjy.common.base.util;



import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xiaolong
 * @version 1.0
 * @description: RSA算法加密
 * @date 2021/10/9 11:25
 */
public class RSAUtils {

    //指定加密算法为RSA
    private static final String KEY_ALGORITHM = "RSA";
    //密钥长度，用来初始化
    private static final int KEYSIZE = 1024;
    //指定公钥存放文件
    private static final String PUBLIC_KEY = "RSAPublickey";
    //指定私钥存放文件
    private static final String PRIVATE_KEY = "RSAPrivatekey";


    /**
     * @param
     * @return
     * @description: 根据 KeyPairGenerator对象生成RSA密钥对 （公钥加密，私钥解密，公钥发给客户端让其对密码加密，私钥用来解密）
     */
    public static Map<String, Object> getKeyPair() throws Exception {
        //为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //利用上面的随机数据源初始化这个KeyPairGenerator对象
        keyPairGen.initialize(KEYSIZE);
        //生成密匙对
        KeyPair keyPair = keyPairGen.generateKeyPair();
        //得到公钥
        PublicKey publicKey = keyPair.getPublic();
        //得到私钥
        PrivateKey privateKey = keyPair.getPrivate();
        Map<String, Object> keyMap = new HashMap<>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }


    //获取私钥
    public static String getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64Util.encode(key.getEncoded());
    }

    //获取公钥
    public static String getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64Util.encode(key.getEncoded());
    }

    //通过公钥对密码进行加密
    public static String encryptByPublicKey(String data, String publicKey) {
        return encryptByPublicKey(data, publicKey, "UTF-8");
    }

    //通过公钥对密码进行加密
    public static String encryptByPublicKey(String data, String publicKey, String charest) {
        try {
            data = Base64Util.encode(encryptByPublicKey(data.getBytes(charest), publicKey));
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }


    //通过私钥解密得到密码
    public static String decryptByPrivateKey(String data, String privatekey) {
        return decryptByPrivateKey(data, privatekey, "UTF-8");
    }


    //通过私钥解密得到密码
    public static String decryptByPrivateKey(String data, String privatekey, String charest) {
        String temp = null;
        try {
            byte[] rs = Base64Util.decode(data);
            temp = new String(decryptByPrivateKey(rs, privatekey), charest);
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * @param
     * @return
     * @description: 通过私钥解密得到密码
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey) throws Exception {
        byte[] keyBytes = Base64Util.decode(privateKey);
        //实例化PKCS8EncodedKeySpec对象（PKCS8EncodedKeySpec类继承EncodedKeySpec类，以编码格式来表示私钥。）
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //实例化KeyFactory对象，并指定DSA算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //获得私钥PrivateKey对象
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        //keyFactory.getAlgorithm()返回算法的名称RSA
        //获取RSA加密cipher对象
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //DECRYPT_MODE 解密模式   ENCRYPT_MODE 加密模式
        //初始化加解密模式和密钥
        cipher.init(Cipher.DECRYPT_MODE, privateK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        //处理多部分解密，当报文长度过长时，采用分块进行加解密；
        for (int i = 0; inputLen - offSet > 0; offSet = i * 128) {
            byte[] cache;
            if (inputLen - offSet > 128) {
                //结束单部分或者多部分加密或者解密操作
                //参数offset为需要加解密的报文byte数组的起始位置，i1为需要加密或者解码的字节长度
                cache = cipher.doFinal(encryptedData, offSet, 128);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
        }
        //toByteArray是ByteArrayOutputStream特有的
        byte[] decrypteData = out.toByteArray();
        out.close();
        return decrypteData;
    }

    /**
     * @param
     * @return
     * @description: 通过公钥进行解密得到密码
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey) throws Exception {
        byte[] keyBytes = Base64Util.decode(publicKey);
        //实例化PKCS8EncodedKeySpec对象（PKCS8EncodedKeySpec类继承EncodedKeySpec类，以编码格式来表示私钥。）
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        //实例化KeyFactory对象，并指定DSA算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //获得私钥PrivateKey对象
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        //keyFactory.getAlgorithm()返回算法的名称RSA
        //获取RSA加密cipher对象
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //DECRYPT_MODE 解密模式   ENCRYPT_MODE 加密模式
        //初始化加解密模式和密钥
        cipher.init(Cipher.DECRYPT_MODE, publicK);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        //处理多部分解密，当报文长度过长时，采用分块进行加解密；
        for (int i = 0; inputLen - offSet > 0; offSet = i * 128) {
            byte[] cache;
            if (inputLen - offSet > 128) {
                //结束单部分或者多部分加密或者解密操作
                //参数offset为需要加解密的报文byte数组的起始位置，i1为需要加密或者解码的字节长度
                cache = cipher.doFinal(encryptedData, offSet, 128);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
        }
        //toByteArray是ByteArrayOutputStream特有的
        byte[] decrypteData = out.toByteArray();
        out.close();
        return decrypteData;
    }


    /**
     * @param
     * @return
     * @description: 通过公钥进行对密码进行加密
     */
    public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
        byte[] keyBytes = Base64Util.decode(publicKey);
        //实例化PKCS8EncodedKeySpec对象（PKCS8EncodedKeySpec类继承EncodedKeySpec类，以编码格式来表示私钥。）
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        //实例化KeyFactory对象，并指定DSA算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //获得公钥PublicKey对象
        Key publicK = keyFactory.generatePublic(x509KeySpec);
        //keyFactory.getAlgorithm()返回算法的名称RSA
        //获取RSA加密cipher对象
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //DECRYPT_MODE 解密模式   ENCRYPT_MODE 加密模式
        //初始化加解密模式和密钥
        cipher.init(Cipher.ENCRYPT_MODE, publicK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        //处理多部分解密，当报文长度过长时，采用分块进行加解密；
        for (int i = 0; inputLen - offSet > 0; offSet = i * 117) {
            byte[] cache;
            if (inputLen - offSet > 117) {
                //结束单部分或者多部分加密或者解密操作
                //参数offset为需要加解密的报文byte数组的起始位置，i1为需要加密或者解码的字节长度
                cache = cipher.doFinal(data, offSet, 117);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
        }
        //toByteArray是ByteArrayOutputStream特有的
        byte[] encrypteData = out.toByteArray();
        out.close();
        return encrypteData;
    }


    /**
     * @param
     * @return
     * @description: 通过私钥对密码进行加密
     */
    public static byte[] encryptByPrivateKey(byte[] data, String privateKey) throws Exception {
        byte[] keyBytes = Base64Util.decode(privateKey);
        //实例化PKCS8EncodedKeySpec对象（PKCS8EncodedKeySpec类继承EncodedKeySpec类，以编码格式来表示私钥。）
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //实例化KeyFactory对象，并指定DSA算法
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        //获得私钥PrivateKey对象
        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
        //keyFactory.getAlgorithm()返回算法的名称RSA
        //获取RSA加密cipher对象
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        //DECRYPT_MODE 解密模式   ENCRYPT_MODE 加密模式
        //初始化加解密模式和密钥
        cipher.init(Cipher.ENCRYPT_MODE, privateK);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        //处理多部分解密，当报文长度过长时，采用分块进行加解密；
        for (int i = 0; inputLen - offSet > 0; offSet = i * 117) {
            byte[] cache;
            if (inputLen - offSet > 117) {
                //结束单部分或者多部分加密或者解密操作
                //参数offset为需要加解密的报文byte数组的起始位置，i1为需要加密或者解码的字节长度
                cache = cipher.doFinal(data, offSet, 117);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
        }
        //toByteArray是ByteArrayOutputStream特有的
        byte[] decrypteData = out.toByteArray();
        out.close();
        return decrypteData;
    }


    public static void main(String[] args) throws Exception {
//        Map<String, Object> keyPair = getKeyPair();
//        String publicKey = getPublicKey(keyPair);
//        String privateKey = getPrivateKey(keyPair);
//        System.out.println("公钥: " + publicKey);
//        System.out.println("私钥: " + privateKey);
//        String data = "123456";
//        String encryptByPublicKey = encryptByPublicKey(data, publicKey);
//        System.out.println("加密后的密码：" + encryptByPublicKey);
//        System.out.println("加密后的长度：" + encryptByPublicKey.length());
//        String decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);
//        System.out.println("解密后的密码：" + decryptByPrivateKey);

        String data = "123456";
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCULima4NpQM/cmeRtFqDgXHe+HMBoZmsoMLzc+QHFBoZGLfYlg+YDLLTwuW6zNDPpFpSpQIHyFxkNfX3qbJaUVABYI+L2i9q7WuIetxatd0qTeTbXPEb8Ds5A5Dpx1XzkLh9pAfnZo9+E5AYZodT51AIKE3qwWVgExsKp+s9/BcQIDAQAB";
        String encryptByPublicKey = encryptByPublicKey(data, publicKey);
        System.out.println("加密后的密码：" + encryptByPublicKey);
        System.out.println("加密后的长度：" + encryptByPublicKey.length());
    }


}
